package main

// go run test.go

import (
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"
	"unsafe"
)

func main() {
	fmt.Println("Hello, World!")
	fmt.Println("Google" + "Runoob")
	// os.Exit(1) //终止代码，中止代码
	// ===========拼接%d 表示整型数字，%s 表示字符串===========
	var stockcode = 123
	var enddate = "2020-12-31"
	var strrr = enddate + " 00:00:00"
	fmt.Println("拼接", strrr)
	var url = "Code=%d&endDate=%s"
	var target_url = fmt.Sprintf(url, stockcode, enddate)
	fmt.Println(target_url)
	var a string = "Runoob"
	fmt.Println(a)

	var b, c int = 1, 2
	fmt.Println(b, c)

	//==============数字转字符串，字符串转数字==========
	//字符串转数字
	numYY, _ := strconv.Atoi("100")
	//数字转字符串
	tableName := strconv.Itoa(numYY % 1000)
	fmt.Print(tableName, "--数字转字符串，字符串转数字--", numYY, "\r\n")

	// ===========声明一个变量并初始化===========
	var aa = "RUNOOB"
	fmt.Println(aa)
	// 没有初始化就为零值
	var bb int
	fmt.Println(bb)
	// bool 零值为 false
	var cc bool
	fmt.Println(cc)

	//===============常量===========
	const LENGTH int = 10
	const WIDTH int = 5
	const (
		Unknown = 0
		Female  = 1
		Male    = 2
	)
	const (
		a2 = "abc"
		b2 = len(a2)
		c2 = unsafe.Sizeof(a2) //字节
	)
	//iota，特殊常量 第一个 iota 等于 0，每当 iota 在新的一行被使用时，它的值都会自动加 1；所以 a3=0, b3=1, c3=2 可以简写为如下形式：
	const (
		a3 = iota //0
		b3        //1
		c3        //2
		d3 = "ha" //独立值，iota += 1
		e3        //"ha"   iota += 1
		f3 = 100  //iota +=1
		g3        //100  iota +=1
		h3 = iota //7,恢复计数
		i3        //8
	)
	var area int
	const a1, b1, c1 = 1, false, "str" //多重赋值
	area = LENGTH * WIDTH
	fmt.Printf("面积为 : %d", area)
	println(a1, b1, c1)
	println(Unknown, Female, Male)
	println(a2, b2, c2)
	println(a3, b3, c3, d3, e3)

	//===============运算符========
	var a4 int = 21
	var b4 int = 10
	var c4 int

	c4 = a4 - b4
	fmt.Printf("第一行 - c 的值为 %d\n", c4)

	//===============面向对象========参考：https: //blog.csdn.net/le_17_4_6/article/details/123991268
	stu1 := new(Student) //新建实例 方式一
	stu1.Speak("momo")
	stu1.Study()

	stu2 := Student{} //新建实例 方式二
	stu2.Speak("momo")
	stu2.Study()

	var stu3 Student //新建实例 方式三
	stu3.Speak("momo")
	stu3.Study()

	//==============语言条件语句/判断语句 https://m.runoob.com/go/go-decision-making.html===========
	/* 定义局部变量 */
	var a5 int = 10
	/* 使用 if 语句判断布尔表达式 */
	if a5 < 20 {
		/* 如果条件为 true 则执行以下语句 */
		fmt.Printf("a5 小于 20\n")
	}
	fmt.Printf("a5 的值为 : %d\n", a5)

	if a5 < 20 { //a5==10
		/* 如果条件为 true 则执行以下语句 */
		fmt.Printf("a5 小于 20\n")
		if a5 == 10 {
			println("a5 == 10\n")
		}
	} else {
		/* 如果条件为 false 则执行以下语句 */
		fmt.Printf("a 不小于 20\n")
	}

	/* 定义局部变量 */
	var grade string = "B"
	var marks int = 90
	switch marks {
	case 90:
		grade = "A"
	case 80:
		grade = "B"
	case 50, 60, 70:
		grade = "C"
	default:
		grade = "D"
	}
	fmt.Printf("你的等级是 %s\n", grade)

	//动态类型，泛类型
	var issv interface{}
	issv = 18
	fmt.Println("动态类型：", issv)
	issv = "Go编程时光"
	fmt.Println("动态类型：", issv)

	//动态变量 实现 动态信道
	chss := make(map[int]chan int)
	for i := 0; i < 2; i++ {
		chss[i] = make(chan int)
	}

	//==============循环语句https://m.runoob.com/go/go-for-loop.html===========
	sum := 0
	for i := 0; i <= 10; i++ {
		sum += i
	}
	fmt.Println("for循环", sum)
	//类似while循环
	sum1 := 1
	for sum1 <= 10 {
		sum1 += sum1
	}
	fmt.Println("for循环", sum1)
	// sum2 := 0
	// for {
	// 	sum2++ // 无限循环下去
	// }
	// fmt.Println(sum2) // 无法输出
	//For-each range 循环,字典json循环
	strings := []string{"google", "runoob"}
	for k, v := range strings {
		fmt.Println(k, v)
	}
	numbers002 := [6]int{1, 2, 3, 5}
	for k, v := range numbers002 {
		fmt.Printf("第 %d 位 x 的值 = %d\n", k, v)
	}
	//for 循环的 range 格式可以省略 key 和 value，如下实例：
	map1 := make(map[int]float32)
	map1[1] = 1.0
	map1[2] = 2.0
	map1[3] = 3.0
	map1[4] = 4.0

	// 读取 key 和 value
	for key, value := range map1 {
		fmt.Printf("key is: %d - value is: %f\n", key, value)
	}

	// 读取 key
	for key := range map1 {
		fmt.Printf("key is: %d\n", key)
	}

	// 读取 value
	for _, value := range map1 {
		fmt.Printf("value is: %f\n", value)
	}

	//golang 的 select 就是监听 IO 操作，当 IO 操作发生时，触发相应的动作每个case语句里必须是一个IO操作，确切的说，应该是一个面向channel的IO操作。
	//参考：https://www.jianshu.com/p/de4bc02e7c72
	// bufChan := make(chan int, 5)
	// go func() {
	// 	time.Sleep(time.Second)
	// 	for {
	// 		<-bufChan
	// 		time.Sleep(5 * time.Second)
	// 	}
	// }()
	// for {
	// 	select {
	// 	case bufChan <- 1:
	// 		fmt.Println("add success")
	// 		time.Sleep(time.Second)
	// 	default:
	// 		fmt.Println("资源已满，请稍后再试")
	// 		time.Sleep(time.Second)
	// 	}
	// }

	// ==============数组 长度 固定不好用1================
	balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

	/* 输出数组元素 */
	var s11 int
	for s11 = 0; s11 < 5; s11++ {
		fmt.Printf("balance[%d] = %f\n", s11, balance[s11])
	}

	//==============切片-比数组灵活 长度 可改变 ================
	//格式：make([]T, length, capacity) capacity可选
	/*
		len():切片是可索引的，并且可以由 len() 方法获取长度。
		cap():切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
		append() :追加切片
		copy():复制切片
	*/
	// 定义切片
	var identifier []int
	//判断切片是否为空
	if identifier == nil {
		fmt.Printf("切片是空的\n")
	}
	/* 允许追加空切片 */
	identifier = append(identifier, 0)
	printSlice(identifier)
	/* 向切片添加一个元素 */
	identifier = append(identifier, 1)
	printSlice(identifier)
	/* 同时添加多个元素 */
	identifier = append(identifier, 2, 3, 4)
	printSlice(identifier)
	/* 创建切片 numbers1 是之前切片的两倍容量*/
	identifier1 := make([]int, len(identifier), (cap(identifier))*2)
	/* 拷贝 identifier 的内容到 identifier1 */
	copy(identifier1, identifier)
	printSlice(identifier1)

	var numbers001 = make([]int, 3, 5)
	printSlice(numbers001)
	/* 创建切片 */
	numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
	printSlice(numbers)
	/* 打印原始切片 */
	fmt.Println("numbers ==", numbers)
	/* 打印子切片从索引1(包含) 到索引4(不包含)*/
	fmt.Println("numbers[1:4] ==", numbers[1:4])
	/* 默认下限为 0*/
	fmt.Println("numbers[:3] ==", numbers[:3])
	/* 默认上限为 len(s)*/
	fmt.Println("numbers[4:] ==", numbers[4:])

	numbers1 := make([]int, 0, 5)
	printSlice(numbers1)
	/* 打印子切片从索引  0(包含) 到索引 2(不包含) */
	number2 := numbers[:2]
	printSlice(number2)
	/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
	number3 := numbers[2:5]
	printSlice(number3)
	//===============Map(集合)  key value键值对类似php array/php数组/make map========================
	/*创建集合
	https://www.cnblogs.com/huangliang-hb/p/10173175.html
	性能：（map性能 单协程 > 多协程 > 通道 ）
	*/
	var countryCapitalMap map[string]string
	countryCapitalMap = make(map[string]string)
	/* map插入key - value对,各个国家对应的首都 */
	countryCapitalMap["France"] = "巴黎"
	countryCapitalMap["Italy"] = "罗马"
	countryCapitalMap["Japan"] = "东京"
	countryCapitalMap["India"] = "新德里"

	/*使用键输出地图值 */
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap[country])
	}

	/*查看元素在集合中是否存在 */
	capital, ok := countryCapitalMap["American"] /*如果确定是真实的,则存在,否则不存在 */
	/*fmt.Println(capital) */
	/*fmt.Println(ok) */
	if ok {
		fmt.Println("American 的首都是", capital)
	} else {
		fmt.Println("American 的首都不存在")
	}

	/* 创建map */
	countryCapitalMap1 := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

	fmt.Println("原始地图")

	/* 打印地图 */
	for country := range countryCapitalMap1 {
		fmt.Println(country, "首都是", countryCapitalMap1[country])
	}

	/*删除元素*/
	delete(countryCapitalMap1, "France")
	fmt.Println("法国条目被删除")

	fmt.Println("删除元素后地图")

	/*打印地图*/
	for country := range countryCapitalMap1 {
		fmt.Println(country, "首都是", countryCapitalMap1[country])
	}

	// ==============指针变量 ，重新赋值新变量，原变量也会变================参考：https://blog.csdn.net/eight_eyes/article/details/119380395
	/* 定义局部变量 */
	var a12 int = 100
	var b12 int = 200

	fmt.Printf("交换前 a12 的值 : %d\n", a12)
	fmt.Printf("交换前 b12 的值 : %d\n", b12)

	/* 调用函数用于交换值
	* &a 指向 a 变量的内存地址
	* &b 指向 b 变量的内存地址
	 */
	swap(&a12, &b12)

	fmt.Printf("交换后 a 的值 : %d\n", a12)
	fmt.Printf("交换后 b 的值 : %d\n", b12)

	// ====================== 结构体， 类定义==================
	// 创建一个新的结构体
	fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})

	// 也可以使用 key => value 格式
	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})

	// 忽略的字段为 0 或 空
	fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})

	var Book1 Books /* 声明 Book1 为 Books 类型 */
	/* book 1 描述 */
	Book1.title = "Go 语言"
	Book1.author = "www.runoob.com"
	Book1.subject = "Go 语言教程"
	Book1.book_id = 6495407

	/* 打印 Book1 信息 */
	fmt.Printf("Book 1 title : %s\n", Book1.title)
	fmt.Printf("Book 1 author : %s\n", Book1.author)
	fmt.Printf("Book 1 subject : %s\n", Book1.subject)
	fmt.Printf("Book 1 book_id : %d\n", Book1.book_id)

	//=================类型转换======================
	var sum0011 int = 17
	var count int = 5
	var mean float32

	mean = float32(sum0011) / float32(count)
	fmt.Printf("mean 的值为: %f\n", mean)

	//=================并发测试======================
	go say("world")
	go say("hello")
	say("wex")

	//==============协程携程测试================
	/*
		goroutine 线程安全问题
		sync.Map 协程map存储 性能太差 谨慎使用，大数据量处理，写入会让协程变慢10倍左右，2秒变20秒
		加锁程序会变慢

		//----读写锁-读写互斥锁----
		var rwMutex sync.RWMutex
		// 读操作
		rwMutex.RLock()
		fmt.Println("读取共享数据")
		rwMutex.RUnlock()
		// 写操作
		rwMutex.Lock()
		fmt.Println("修改共享数据")
		rwMutex.Unlock()

		//----互斥锁（普通锁）----
		var mutex sync.Mutex
		mutex.Lock()
		mutex.Unlock()

		线程安全计数器原子操作 比加琐性能好-缺点是只能存在一个数字
		var count int64
		go func(){
			atomic.StoreInt64(&count, 1)//给count加1  count++
		}()
		fmt.Println(count)

		协程存储变量问题，7307114万数据slice转map去重复
		单线程:4秒
		信道:16秒
		sync.Map：36.6431181s
		互斥锁:15秒
		读写锁:15秒
	*/
	var wg sync.WaitGroup
	for ii := 0; ii < 10; ii++ {
		ii1 := ii
		wg.Add(1) //+1次
		go func(ii interface{}) {
			defer wg.Done() //减一次
			fmt.Println("协程", ii, ii1)
		}(ii)
	}
	wg.Wait()
	time.Sleep(time.Second)

	//===========channel-多线程通信,用于通信=============
	// var identifier chan datatype
	/*参考：https://blog.csdn.net/qq_32828933/article/details/127957394
	通道关闭方法
	ch := make(chan string)
	ch <- value

	for {
		v1, ok := <-ch
		res[v1] = struct{}{}
		count--
		// fmt.Println(v1, count, ok, len(res))
		if ok && count <= 0 {//ok==true
			close(ch)
			break
		}
	}
	*/
	// 操作符<-
	ch := make(chan int)
	go func() {
		fmt.Println("hello from goroutine")
		ch <- 101 //数据写入channel
	}()
	iiis := <-ch //从channel中取数据并赋值
	println("取数据", iiis)

	//通道缓冲
	ch1 := make(chan int, 10)
	go func() {
		for i := 0; i < 10; i++ {
			rand.Seed(time.Now().UnixNano())
			n := rand.Intn(10) // n will be between 0 and 10
			fmt.Println("putting: ", n)
			ch1 <- n
		}
		close(ch1) //关闭通道
	}()
	fmt.Println("hello from main")
	//遍历通道
	// range 函数遍历每个从通道接收到的数据，因为 c 在发送完 10 个
	// 数据之后就关闭了通道，所以这里我们 range 函数在接收到 10 个数据
	// 之后就结束了。如果上面的 c 通道不关闭，那么 range 函数就不
	// 会结束，从而在接收第 11 个数据的时候就阻塞了。
	for v := range ch1 {
		fmt.Println("receiving: ", v)
	}

}

// *内存地址 取值
func swap(x *int, y *int) {
	var temp int
	temp = *x /* 保存 x 地址的值 */
	*x = *y   /* 将 y 赋值给 x */
	*y = temp /* 将 temp 赋值给 y */
}

// ====================== 结构体==================
// 类继承使用Investigate\repository\baseRepo.go
// Go 的 type ≈ PHP 的 class 或 类型定义
type Books struct {
	title   string
	author  string
	subject string
	book_id int
}

/*
Go 的 func ≈ PHP 的 function
*/
func printSlice(x []int) {
	fmt.Printf("切片：len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

// 并发测试
func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(200 * time.Millisecond)
		fmt.Println(s)
	}
}

// 面向对象
type Student struct{}

func (stu *Student) Speak(name string) (talk string) {
	talk = "您好，" + name
	return
}
func (stu Student) Study() {
	fmt.Println("我在学习")
}
